package JavaCollection;




import org.junit.jupiter.api.Test;

import java.util.*;

/**
 * @author Lenl
 * @version v1.0
 * @create 2021-06-05 9:38
 * @description Set接口的框架结构
 * 1.Set接口框架
 * |--Collection接口：单列集合，用来存储一个个的对象
 *     |--Set:无序不可重复-->“高中数学中的集合”
 *          |--HashSet：作为Set接口的主要实现类，线程不安全，可存储null值
 *              |--LinkedHashSet：作为HashSet的子类，遍历内部数据时可以按添加顺序遍历
 *          |--TreeSet：可以按照添加对象的指定属性，进行排序
 *  Set接口中没有定义新方法，用的都是Collection的方法
 */
public class SetTest {
    /**
     * 一、Set：存储无序的、不可重复的数据
     * 以HashSet为例
     *  1.无序性：不等于随机性。存储的数据在底层数组中并非按照数组索引添加，而是按照数据的哈希值添加
     *
     *
     *  2.不可重复性：
     *      不可重复性保证添加的元素按照equals()判断时，不能返回true，即相同元素只能添加一个
     * 二、添加元素的过程：以HashSet为例
     *      1.我们向HashSet中添加a，首先调用a所在类的hashCode()，计算a的哈希值，
     *      2.此时哈希值接着通过某种算法计算出在HashSet底层的存放位置(索引)，判断数组此位置是否有其他元素
     *          2.1没有其他元素：元素a添加成功-->情况1
     *          2.2有其他元素b（或以链表形式存在多个元素），比较a与b元素的hash值：
     *              2.2.1哈希值不同，a添加成功-->情况2
     *              2.2.2哈希值相同，调用a所在类的equals()方法，
     *                  2.2.2.1返回true:元素a添加失败-->情况3
     *                  2.2.2.2返回false:元素a添加成功-->情况4
     *      对于添加成功的情况2和情况4来说，元素a与已存在指定索引位置上的数据以链表方式存储
     *      jdk7:元素a放到数组中，指向原来的元素
     *      jdk8：原来的元素在数组中，指向元素a
     *      总结：七上八下
     *      HashSet底层：数组+链表的结构
     *
     *  要求：1.向Set中添加的元素必须重写hashCode()和equals()方法
     *       2.重写的hashCode()和equals()尽可能保持一致性：相等对象必须具有相同的散列码
     *      小技巧：用idea重写时自动生成的
     */

    @Test
    public void  test1(){
        Set set=new HashSet();
        set.add(123);
        set.add("AAB");
        set.add("CCd");
        set.add(new Person("tom"));
        set.add(new Person("tom")); //哈希值不同，能够添加,或哈希相同，equals不同能添加
        set.add(false);
        set.add("CCd");//不可重复，此项不添加

        //保证不重复
        set.add(new User("mark",21));
        set.add(new User("mark",21)); //这里重写了equals方法和hashCode方法，添加不成功

        Iterator iterator=set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    @Test
    public void test2(){
        /**
         * LinkedHashSet底层仍是无序的，只是在遍历时是有序的
         *  在添加数据同时维护了两个引用，记录了前后数据
         *      对于频繁的遍历操作，HashSet效率更高
         */
        Set set=new LinkedHashSet();
        set.add(123);
        set.add("AAB");
        set.add("CCd");
        set.add(new Person("tom"));
        set.add(new Person("tom")); //哈希值不同，能够添加,或哈希相同，equals不同能添加
        set.add(false);
        set.add("CCd");//不可重复，此项不添加

        //保证不重复
        set.add(new User("mark",21));
        set.add(new User("mark",21)); //这里重写了equals方法和hashCode方法，添加不成功

        Iterator iterator=set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

}

 class User implements Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        int result=name!=null?name.hashCode():0;
        result=31*result+age;
        return result;
    }

    @Override
    public int compareTo(Object o) {
        //按姓名从小到大排 年龄从小到大排
        if(o instanceof  User){
            User u=(User)o;
            int compare= this.name.compareTo(u.name);
            if(compare!=0) return compare;
            else return Integer.compare(this.age,u.age);
        }else{
            throw  new RuntimeException("输入类型不匹配");
        }
    }
}